﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace WPFDrawingContext
{
    public class ChartCanvas: FrameworkElement
    {
        private readonly DrawingVisual drawingVisual = new DrawingVisual();
        private readonly VisualCollection visuals;
        private readonly Pen linePen = new Pen(Brushes.Blue, 1);
        private List<Point> dataPoints;
        private double scale = 1.0;
        private Point offset = new Point(0, 0);

        public ChartCanvas()
        {
            visuals = new VisualCollection(this) { drawingVisual };
            this.dataPoints = GenerateDataPoints(100000);
            this.Loaded += (s, e) => RenderChart();
            this.SizeChanged += (s, e) => RenderChart();
            this.MouseWheel += OnMouseWheel;
            this.MouseDown += OnMouseDown;
            this.MouseMove += OnMouseMove;
            this.MouseUp += OnMouseUp;
        }

        private List<Point> GenerateDataPoints(int count)
        {
            var points = new List<Point>();
            Random random = new Random();
            double width = 2000;
            double height = 500;
            for (int i = 0; i < count; i++)
            {
                double x = i * (width / (double)count);
                double y = height / 2 + Math.Sin(i * 0.01) * 100 + random.Next(-10, 10);
                points.Add(new Point(x, y));
            }
            return points;
        }
        private void RenderChart()
        {
            if (ActualWidth == 0 || ActualHeight == 0) return;

            using (DrawingContext dc = drawingVisual.RenderOpen())
            {
                dc.DrawRectangle(Brushes.White, null, new Rect(0, 0, ActualWidth, ActualHeight));

                if (dataPoints.Count > 1)
                {
                    var geometry = new StreamGeometry();
                    using (StreamGeometryContext ctx = geometry.Open())
                    {
                        bool started = false;
                        foreach (var point in dataPoints)
                        {
                            double x = (point.X - offset.X) * scale;
                            double y = (point.Y - offset.Y) * scale;

                            if (x < 0 || x > ActualWidth) continue;

                            if (!started)
                            {
                                ctx.BeginFigure(new Point(x, y), false, false);
                                started = true;
                            }
                            else
                            {
                                ctx.LineTo(new Point(x, y), true, false);
                            }
                        }
                    }
                    dc.DrawGeometry(null, linePen, geometry);
                }
            }
            InvalidateVisual();
        }
        private void OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (dataPoints.Count == 0) return;
            Point mousePos = e.GetPosition(this); // 获取鼠标相对控件的坐标
            double oldX = (mousePos.X + offset.X) / scale;
            double oldY = (mousePos.Y + offset.Y) / scale;
            scale += e.Delta > 0 ? 0.1 : -0.1;
            scale = Math.Max(0.1, Math.Min(10, scale)); // 限制缩放范围
            double newX = (mousePos.X + offset.X) / scale;
            double newY = (mousePos.Y + offset.Y) / scale;
            offset.X += (newX - oldX) * scale;
            offset.Y += (newY - oldY) * scale;
            RenderChart();
        }

        private Point lastMousePos;
        private bool isPanning = false;

        private void OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                isPanning = true;
                lastMousePos = e.GetPosition(this);
                Mouse.Capture(this);
            }
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (isPanning && e.LeftButton == MouseButtonState.Pressed)
            {
                Point newPos = e.GetPosition(this);
                offset.X -= (newPos.X - lastMousePos.X) / scale;
                offset.Y -= (newPos.Y - lastMousePos.Y) / scale;
                lastMousePos = newPos;
                RenderChart();
            }
        }

        private void OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            isPanning = false;
            Mouse.Capture(null);
        }

        protected override int VisualChildrenCount => visuals.Count;

        protected override Visual GetVisualChild(int index) => visuals[index];

    }
}
